#include "SmtpBase.h"

#include <cstring>
#include <errno.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory.h>
#include <memory>
#include <ostream>

namespace fs = std::filesystem;

namespace smtp {
    SmtpBase::SmtpBase() {
    }

    void SmtpBase::send_file(std::string filepath_) {
        std::ifstream fin;
        fin.open(filepath_, std::ios_base::binary);
        if (!fin.is_open()) {
            std::cerr << "send_file:" << filepath_ << ":" << std::strerror(errno) << std::endl;
            return;
        }
        fs::path p(filepath_);
        std::string fname  = p.filename().string();
        std::string attach = "--INVT\r\nContent-Type: application/octet-stream;\r\n"
                             "  name=\"" +
                             fname + "\"\r\nContent-Transfer-Encoding: base64\r\n"
                                     "Content-Disposition: attachment;\r\n  filename=\"" +
                             fname + "\"\r\n\r\n";

        send(attach);
        int length = fs::file_size(p);

        auto buffer = std::shared_ptr<char[]>(new char[length]);
        fin.read(buffer.get(), length);
        std::string fc_base = base64_encode(buffer.get(), length);
        send(fc_base + "\r\n");
        fin.close();
    }

    const std::string SmtpBase::base64_encode(const char* bytes_to_encode, unsigned int in_len) const {
        std::string ret;
        int i = 0;
        int j = 0;
        unsigned char char_array_3[3];
        unsigned char char_array_4[4];

        while (in_len--) {
            char_array_3[i++] = *(bytes_to_encode++);
            if (i == 3) {
                char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
                char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
                char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
                char_array_4[3] = char_array_3[2] & 0x3f;

                for (i = 0; (i < 4); i++)
                    ret += base64_chars[char_array_4[i]];
                i = 0;
            }
        }

        if (i) {
            for (j = i; j < 3; j++)
                char_array_3[j] = '\0';

            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
            char_array_4[3] = char_array_3[2] & 0x3f;

            for (j = 0; (j < i + 1); j++)
                ret += base64_chars[char_array_4[j]];

            while ((i++ < 3))
                ret += '=';
        }
        return ret;
    }

    bool SmtpBase::login(std::string username, std::string password) {
        // 登录
        send("auth login\r\n");
        std::cout << get_response() << std::endl;

        std::string user_base64 = base64_encode(username.data(),
            username.length());
        std::string pass_base64 = base64_encode(password.data(),
            password.length());

        send(user_base64 + "\r\n");
        std::cout << get_response() << std::endl;
        send(pass_base64 + "\r\n");
        std::cout << get_response() << std::endl;

        return true;
    }

    bool SmtpBase::mail(const AbstractMail* mail) {
        /*
        SMTP 协议规定的顺序通常是：
            发送 HELO 或 EHLO 命令。
            发送发件人地址 MAIL FROM 命令。
            发送收件人地址 RCPT TO 命令。
            发送 DATA 命令，表示开始发送邮件内容。
            发送邮件内容，包括头信息和正文。
            发送结束符 .，表示邮件内容发送完毕。
            发送 QUIT 命令，结束 SMTP 会话。
        */

        send("HELO " + mail->get_form() + "\r\n");
        std::cout << "HELO " << get_response() << std::endl;

        send("mail from: <" + mail->get_form() + ">\r\n");
        std::cout << "mail from " << get_response() << std::endl;

        send("rcpt to: <" + mail->get_to() + ">\r\n");
        std::cout << "rcpt to " << get_response() << std::endl;

        send("data\r\n");
        std::cout << get_response() << std::endl;

        send("From:" + mail->get_form() + "\r\n");
        // std::cout << get_response() << std::endl;

        send("subject:" + mail->get_subject() + "\r\nMime-Version: 1.0\r\n"
                                                "Content-Type: multipart/mixed;   "
                                                "boundary=\"INVT\"\r\n\r\n");
        // std::cout << get_response() << std::endl;

        send("--INVT\r\n"
             "Content-Type: " +
             mail->get_content_type() +
             ";\r\n  "
             "charset=\"utf8\"\r\n\r\n" +
             mail->get_content() +
             "\r\n\r\n");
        // std::cout << get_response() << std::endl;

        auto files = mail->get_attachfiles();
        for (auto&& i : files) {
            send_file(i);
        }

        send("\r\n.\r\n");
        std::cout << get_response() << std::endl;
        // send("quit\r\n");
        // std::cout << get_response() << std::endl;
        return true;
    }

    bool SmtpBase::quit() {
        send("quit\r\n");
        std::cout << get_response() << std::endl;
        return true;
    }

}  // namespace smtp
